package test.main;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.math3.stat.descriptive.DescriptiveStatistics;

import com.google.common.base.Function;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Ordering;

public class HistoryChecker {
	
	private File input;
	private List<List<Integer>> rawHistory = new ArrayList<>();
	private List<List<Double>> historic = new ArrayList<>();
	private List<DescriptiveStatistics> stats = new ArrayList<>();
	
	Double minMean; Double maxMean;
	Double minGMean; Double maxGMean;
	Double minSD; Double maxSD;
	Double minVar; Double maxVar;
	
	int matchCounter = 0;
	
	private Map<Integer, Integer> distribution = new TreeMap<>();
	
	public HistoryChecker(String file) {
		try {
			input = new File(file);
			BufferedReader reader = new BufferedReader(new FileReader(input));
			String line = "";
			while ((line = reader.readLine()) != null) {
				String[] token = line.split(",");
				List<Integer> rawRow = new ArrayList<>();
				List<Double> row = new ArrayList<>();
				for (String s : token) {
					Integer num = Integer.parseInt(s.trim());
					if (!distribution.containsKey(num)) distribution.put(num, 0);
					distribution.put(num, distribution.get(num)+1);
					rawRow.add(num);
					row.add(Double.parseDouble(s.trim()));
				}
				rawHistory.add(rawRow);
				historic.add(row);
				stats.add(new DescriptiveStatistics(ArrayUtils.toPrimitive(row.toArray(new Double[]{}))));
			}
			reader.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
		log();
	}
	
	private void log() {
		List<Double> means = getMean();
		List<Double> gMean = getGeometricMean();
		List<Double> sd = getStandardDeviation();
		List<Double> var = getVariance();
		
//		Collections.sort(means);
//		Collections.sort(gMean);
//		Collections.sort(sd);
//		Collections.sort(var);
		
		System.out.println("Means: "+means);
		System.out.println("Geometric Means: "+gMean);
		System.out.println("SD: "+sd);
		System.out.println("VAR: "+var);
		
		System.out.println();
		
		System.out.println("Mean range: "+minMean+" : "+maxMean);
		System.out.println("Geometric Mean range: "+minGMean+" : "+maxGMean);
		System.out.println("SD range: "+minSD+" : "+maxSD);
		System.out.println("VAR range: "+minVar+" : "+maxVar);
		
		System.out.println();
		
		System.out.println("Distribution size="+getDistribution().size()+" : "+getDistribution());
		
		System.out.println();
	}
	
	public List<Double> getMean() {
		List<Double> result = new ArrayList<>();
		for (DescriptiveStatistics stat : stats) {
			result.add(stat.getMean());
		}
		
		minMean = Collections.min(result);
//		minMean = 20D;
		maxMean = Collections.max(result);
		
		return result;
	}
	
	public List<Double> getGeometricMean() {
		List<Double> result = new ArrayList<>();
		for (DescriptiveStatistics stat : stats) {
			result.add(stat.getGeometricMean());
		}
		
		minGMean = Collections.min(result);
		maxGMean = Collections.max(result);
		
		return result;
	}
	
	public List<Double> getStandardDeviation() {
		List<Double> result = new ArrayList<>();
		for (DescriptiveStatistics stat : stats) {
			result.add(stat.getStandardDeviation());
		}
		
		minSD = Collections.min(result);
		maxSD = Collections.max(result);
		
		return result;
	}
	
	public List<Double> getVariance() {
		List<Double> result = new ArrayList<>();
		for (DescriptiveStatistics stat : stats) {
			result.add(stat.getVariance());
		}
		
		minVar = Collections.min(result);
		maxVar = Collections.max(result);
		
		return result;
	}
	
	public boolean checkHistory(double[] numbers) {
		DescriptiveStatistics stat = new DescriptiveStatistics(numbers);
		double mean = stat.getMean();
		double gMean = stat.getGeometricMean();
		double sd = stat.getStandardDeviation();
		double var = stat.getVariance();
		
		boolean meanOK = (minMean <= mean && mean <= maxMean);
		boolean gMeanOK = (minGMean <= gMean && gMean <= maxGMean);
		boolean sdOK = (minSD <= sd && sd <= maxSD);
		boolean varOK = (minVar <= var && var <= maxVar);
		
		return meanOK && gMeanOK && sdOK && varOK;
	}
	
	public boolean checkMatch(int[] numbers) {
		boolean result = false;
		for (List<Integer> row : rawHistory) {
			boolean match = true;
			for (int number : numbers) {
				if (!row.contains(number))
					match = false;
			}
			if (match) {
				result = true;
				matchCounter++;
				System.out.println("Found match: "+row);
			}
		}
		return result;
	}
	
	public int getMatch() {
		return matchCounter;
	}
	
	public int getSampleSize() {
		return rawHistory.size();
	}
	
	public void printStats(List<Integer> sample) {
		double[] numbers = new double[sample.size()];
		for (int i=0; i<sample.size(); i++) {
			numbers[i] = sample.get(i);
		}
		
		DescriptiveStatistics stat = new DescriptiveStatistics(numbers);
		System.out.println("Mean: "+stat.getMean());
		System.out.println("Geometric Mean: "+stat.getGeometricMean());
		System.out.println("SD: "+stat.getStandardDeviation());
		System.out.println("VAR: "+stat.getVariance());
	}
	
	public Map<Integer, Integer> getDistribution() {
		Ordering<Map.Entry<Integer, Integer>> entryOrdering = Ordering.natural().onResultOf(new Function<Entry<Integer, Integer>, Integer>() {

			@Override
			public Integer apply(Entry<Integer, Integer> arg0) {
				return arg0.getValue();
			}}).reverse();
		
		ImmutableMap.Builder<Integer, Integer> builder = ImmutableMap.builder();
		for (Entry<Integer, Integer> entry : entryOrdering.sortedCopy(distribution.entrySet())) {
		  builder.put(entry.getKey(), entry.getValue());
		}
		return builder.build();
	}
}
